﻿
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using suirui.ZhuMu.Common;
using suirui.ZhuMu.Log;
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Results;

namespace suirui.ZhuMu.WebCore
{
    /// <summary>
    /// 数据解密过滤器
    /// 前端只对post请求接口进行加密（先用非对称加密方式（RSA）加密对称加密的密钥，然后对称加密（AES）数据包）
    /// </summary>
    public class DataDecryptFilter : ActionFilterAttribute
    {
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var noEncrypt = AppConfigration.Configuration["NoEncrypt"];//白名单
            var method = context.HttpContext.Request.Method;
            if (method == "POST" && !noEncrypt.Contains(context.HttpContext.Request.Path))
            {
                string aeskey = context.HttpContext.Request.Headers["aeskey"];
                if (string.IsNullOrWhiteSpace(aeskey))
                {
                    return;
                }
                if (aeskey.Length > 36)//判断aeskey是否已经解密，若未解密则先进行解密操作
                {
                    var rsaHelper = new RSAUtils(RSAType.RSA2, Encoding.UTF8);
                    aeskey = rsaHelper.Decrypt(aeskey);
                }

                var request = context.HttpContext.Request;
                Stream stream = request.Body;
                Encoding encoding = Encoding.UTF8;
                string responseData = string.Empty;
                using (StreamReader reader=new StreamReader(stream,encoding))
                {
                    responseData = reader.ReadToEndAsync().Result;
                    //responseData = reader.ReadToEnd().ToString();
                }


                //解密数据包
                var data = context.HttpContext.Request.Form.FirstOrDefault().Value;
                //var data = context.ActionArguments.Values.FirstOrDefault();
                var dataJson = AESUtils.AESDecrypt(data, aeskey);

                if (string.IsNullOrWhiteSpace(dataJson))
                {
                    context.Result = new Microsoft.AspNetCore.Mvc.BadRequestResult();
                    return;
                }
                if (context.ActionArguments.Values.Count > 0)
                {
                    //-----------model接收模式----------//
                    var type = context.ActionArguments.Values.ToList()[0].GetType();

                    PropertyInfo[] ps = type.GetProperties();

                    var model = context.ActionArguments.Values.ToList()[0];

                    var dy = JsonConvert.DeserializeObject(dataJson, type);

                    var type2 = dy.GetType();
                    PropertyInfo[] ps2 = type2.GetProperties();

                    foreach (PropertyInfo i in ps)
                    {
                        foreach (PropertyInfo i2 in ps2)
                        {
                            var value = i2.GetValue(dy, null);
                            if (i.Name == i2.Name && value != null)
                            {
                                i.SetValue(model, value, null);
                            }
                        }
                    }
                }
                else
                {
                    //-----------变量接收模式----------//
                    var dy = (JObject)JsonConvert.DeserializeObject(dataJson);
                    var parameterslist = context.ActionDescriptor.Parameters.ToList();
                    foreach (var item in parameterslist)
                    {
                        if (dy[item.Name] == null)
                            continue;
                        var vaule = ConvertObject(dy[item.Name].ToString(), item.ParameterType);
                        context.ActionArguments.Add(item.Name, vaule);
                    }
                }
            }
            await base.OnActionExecutionAsync(context, next);
        }


        /// <summary>
        /// 将一个对象转换为指定类型
        /// </summary>
        /// <param name="obj">待转换的对象</param>
        /// <param name="type">目标类型</param>
        /// <returns>转换后的对象</returns>
        private object ConvertObject(object obj, Type type)
        {
            if (type == null) return obj;
            if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;

            Type underlyingType = Nullable.GetUnderlyingType(type);
            if (type.IsAssignableFrom(obj.GetType())) // 如果待转换对象的类型与目标类型兼容，则无需转换
            {
                return obj;
            }
            else if ((underlyingType ?? type).IsEnum) // 如果待转换的对象的基类型为枚举
            {
                if (underlyingType != null && string.IsNullOrEmpty(obj.ToString())) // 如果目标类型为可空枚举，并且待转换对象为null 则直接返回null值
                {
                    return null;
                }
                else
                {
                    return Enum.Parse(underlyingType ?? type, obj.ToString());
                }
            }
            else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type)) // 如果目标类型的基类型实现了IConvertible，则直接转换
            {
                try
                {
                    return Convert.ChangeType(obj, underlyingType ?? type, null);
                }
                catch
                {
                    return underlyingType == null ? Activator.CreateInstance(type) : null;
                }
            }
            else
            {
                TypeConverter converter = TypeDescriptor.GetConverter(type);
                if (converter.CanConvertFrom(obj.GetType()))
                {
                    return converter.ConvertFrom(obj);
                }
                ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
                if (constructor != null)
                {
                    object o = constructor.Invoke(null);
                    PropertyInfo[] propertys = type.GetProperties();
                    Type oldType = obj.GetType();
                    foreach (PropertyInfo property in propertys)
                    {
                        PropertyInfo p = oldType.GetProperty(property.Name);
                        if (property.CanWrite && p != null && p.CanRead)
                        {
                            property.SetValue(o, ConvertObject(p.GetValue(obj, null), property.PropertyType), null);
                        }
                    }
                    return o;
                }
            }
            return obj;
        }
    }

    public class CustomRequestAuthorizeAttribute : AuthorizeAttribute
    {

        public override void OnAuthorization(HttpActionContext actionContext)
        {
            //action具有[AllowAnonymous]特性不参与验证
            if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>().Any(x => x is AllowAnonymousAttribute))
            {
                base.OnAuthorization(actionContext);
                return;
            }
            var request = actionContext.Request;
            string method = request.Method.Method, timeStamp = string.Empty, expireyTime = AppConfigration.Configuration["UrlExpireTime"], timeSign = string.Empty, platformType = string.Empty;
            if (!request.Headers.Contains("timesign") || !request.Headers.Contains("platformtype") || !request.Headers.Contains("timestamp") || !request.Headers.Contains("expiretime"))
            {
                HandleUnauthorizedRequest(actionContext);
                return;
            }
            platformType = request.Headers.GetValues("platformtype").FirstOrDefault();
            timeSign = request.Headers.GetValues("timesign").FirstOrDefault();
            timeStamp = request.Headers.GetValues("timestamp").FirstOrDefault();
            var tempExpireyTime = request.Headers.GetValues("expiretime").FirstOrDefault();
            //string privateKey = Encoding.UTF8.GetString(Convert.FromBase64String(ConfigurationManager.AppSettings[$"PlatformPrivateKey_{platformType}"]));
            if (!SignValidate(tempExpireyTime, timeStamp, timeSign))
            {
                HandleUnauthorizedRequest(actionContext);
                return;
            }
            if (tempExpireyTime != "0")
            {
                expireyTime = tempExpireyTime;
            }
            //判断timespan是否有效
            double ts2 = Math.Round((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds, 2), ts = ts2 - Convert.ToDouble(timeStamp);
            bool falg = ts > int.Parse(expireyTime) * 1000;
            if (falg)
            {
                HandleUnauthorizedRequest(actionContext);
                return;
            }
            base.IsAuthorized(actionContext);
        }
        protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
        {
            base.HandleUnauthorizedRequest(filterContext);

            var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage();
            response.StatusCode = HttpStatusCode.Forbidden;
            var content = new
            {
                BusinessStatus = -10403,
                StatusMessage = "服务端拒绝访问"
            };
            response.Content = new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8, "application/json");
        }
        private bool SignValidate(string expiryTime, string timestamp, string sign)
        {
            bool isValidate = false;
            var rsaHelper = new RSAUtils(RSAType.RSA2, Encoding.UTF8);
            var tempSign = rsaHelper.Decrypt(sign);
            if (SHA256Utils.SHA256Encrypt($"expiretime{expiryTime}" + $"timestamp{timestamp}").ToString() == tempSign)
            {
                isValidate = true;
            }
            return isValidate;
        }
    }

    public class CustomerMessageProcesssingHandler : MessageProcessingHandler
    {
        protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var contentType = request.Content.Headers.ContentType;

            if (!request.Headers.Contains("platformtype"))
            {
                return request;
            }
            var rsaHelper = new RSAUtils(RSAType.RSA2, Encoding.UTF8);
            //根据平台编号获得对应私钥
            //string privateKey = Encoding.UTF8.GetString(Convert.FromBase64String(ConfigurationManager.AppSettings["PlatformPrivateKey_" + request.Headers.GetValues("platformtype").FirstOrDefault()]));
            if (request.Method == HttpMethod.Post)
            {
                // 读取请求body中的数据
                string baseContent = request.Content.ReadAsStringAsync().Result;
                // 获取加密的信息
                // 兼容 body: 加密数据  和 body: sign=加密数据
                baseContent = Regex.Match(baseContent, "(sign=)*(?<sign>[\\S]+)").Groups[2].Value;
                // 用加密对象解密数据                
                baseContent = rsaHelper.Decrypt(baseContent);//privateKey
                // 将解密后的BODY数据 重置
                request.Content = new StringContent(baseContent);
                //此contentType必须最后设置 否则会变成默认值
                request.Content.Headers.ContentType = contentType;
            }
            if (request.Method == HttpMethod.Get)
            {
                string baseQuery = request.RequestUri.Query;
                // 读取请求 url query数据
                baseQuery = baseQuery.Substring(1);
                baseQuery = Regex.Match(baseQuery, "(sign=)*(?<sign>[\\S]+)").Groups[2].Value;
                baseQuery = rsaHelper.Decrypt(baseQuery);//privateKey
                // 将解密后的 URL 重置URL请求
                request.RequestUri = new Uri($"{request.RequestUri.AbsoluteUri.Split('?')[0]}?{baseQuery}");
            }
            return request;
        }
        protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
        {
            return response;
        }
    }
}
